package com.benmei.weike.service;

import com.benmei.weike.common.*;
import com.benmei.weike.config.SmallProgramParameter;
import com.benmei.weike.dao.*;
import com.benmei.weike.dto.PageRequest;
import com.benmei.weike.dto.PageResponse;
import com.benmei.weike.dto.payment.CreateOrderRequest;
import com.benmei.weike.dto.wechatSmallProgram.payment.*;
import com.benmei.weike.entity.*;
import com.benmei.weike.exception.ClientException;
import com.benmei.weike.exception.ServerException;
import com.benmei.weike.paypal.GetAccessToken;
import com.benmei.weike.paypal.GetPaymentDetail;
import com.benmei.weike.util.*;
import com.nativetalk.base.RetInfo;
import com.nativetalk.bean.teacher.TdNtTeaFoundChange;
import com.nativetalk.bean.teacher.TdNtTeacherResult;
import com.pingplusplus.Pingpp;
import com.pingplusplus.exception.*;
import com.pingplusplus.model.Charge;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.StatusLine;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicNameValuePair;
import org.apache.ibatis.session.RowBounds;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.xml.bind.DatatypeConverter;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.net.URL;
import java.net.URLConnection;
import java.sql.Timestamp;
import java.util.*;

/**
 * Created by Peter on 2017/9/18.
 */
@Service
public class PaymentService {

    public static final Logger logger = LoggerFactory.getLogger(PaymentService.class);

    @Value("${Ping++.APP_ID}")
    public String pingAppId;
    @Value("${Ping++.Secret_Key}")
    public String pingSecretKey;
    @Value("${Ping++.Test}")
    public String pingTest;

    @Value("${Paypal.Client_ID}")
    public String paypalAppId;
    @Value("${Paypal.Secret_Key}")
    public String paypalSecretKey;

    @Value("${pay.detail.url}")
    public String payDetailUrl;
    @Value("${oath2.token.url}")
    public String oath2TokenUrl;
    @Resource
    private SmallProgramParameter smallProgramParameter;

    @Resource
    private WechatSmallProgramService wechatSmallProgramService;

    @Resource
    private WeikeDao weikeDao;

    @Resource
    private TdNtTeaFoundChangeDao tdNtTeaFoundChangeDao;

    @Resource
    private TdNtTeacherDao tdNtTeacherDao;

    @Resource
    private PaymentDao paymentDao;

    @Resource
    private StudentDao studentDao;

    @Resource
    private OrderDao orderDao;

    @Resource
    private BillDao billDao;

    @Resource
    private WeikeStudentBuyDao weikeStudentBuyDao;

    @Resource
    private WeikeSubscriptionDao weikeSubscriptionDao;

    @Resource
    private ChargeNotifyLogDao chargeNotifyLogDao;

    @Resource
    private WeiChatPayNotifyRequestDao weiChatPayNotifyRequestDao;

    @Resource
    private SystemConfigDao systemConfigDao;

    @Autowired
    private WeikeTeacherHourChargeDao weikeTeacherHourChargeDao;

    @Autowired
    private PaypalLogDao paypalLogDao;

    /**
     * 创建订单
     *
     * @param req
     * @param memb_id
     * @param ip
     */
    @Transactional
    public RetInfo studentCreateOrder(CreateOrderRequest req, Integer memb_id, String ip) throws ClientException {
        logger.info("pingAppId:" + pingAppId + ", pingSecretKey:" + pingSecretKey + ", pingTest:" + pingTest);
        RetInfo retInfo = RetInfo.getSuccessInfo("订单创建成功");
        Map<String, Object> mcharge = new HashMap<>();
        Charge charge = null;
        try {
            Student student = studentDao.getById(memb_id);

            // 1. 创建账单
            Bill bill = createBill(req, student, ip, PayThirdPart.PingPlusPlus);

            // 余额支付
            if (PayType.WALLET.equals(req.getPayType())) {
                bill.setPaymentStatus(PaymentConstants.Status.SUCCESS);

                // 2. 保存账单
                billDao.insert(bill);

                // 3. 变更余额
                paymentDao.updateMenberBalance(student.getMemb_id(), bill.getBalanceAfter());

                // 4. 添加商品
                addGoods(bill);
                mcharge.put("charge", charge);
                retInfo.setObj(mcharge);
            }
            // 第三方支付 或 组合支付（余额 + 第三方）
            else {
                if(req.getChannel().equals(Constants.PaymentChannel.paypal_name)){
                    bill.setOrderNo(req.getPaypalId());
                    bill.setThirdPart(req.getChannel());
                    // 保存账单
                    billDao.insert(bill);
                    //查看支付状态
                    StringBuffer logAppend = new StringBuffer();
                    GetPaymentDetail getPaymentDetail = paypalDetail(req.getPaypalId());
                    retInfo.setTip(getPaymentDetail.getTransactions().get(0).getRelated_resources().get(0).getSale().getState());
                    Integer newStatus = getPaymentDetail.getTransactions().get(0).getRelated_resources().get(0).getSale().getState().equals("completed") ? PaymentConstants.Status.SUCCESS : PaymentConstants.Status.UN_PAY;
                    //添加日志
                    PaypalLog paypalLog = new PaypalLog(getPaymentDetail);
                    paypalLogDao.insertSeletive(paypalLog);
                    //添加商品
                    processPayNotifyBusiness(logAppend,true,bill,bill.getPaymentStatus(),newStatus,bill.getChannel());
                }else{
                    // 2. 创建订单
                    charge = createOrder(bill);
                    // 3. 保存账单
                    billDao.insert(bill);
                }
                    // 添加商品相关的业务逻辑在Ping++ Notify回调方法里处理
                mcharge.put("charge", charge);
                retInfo.setObj(mcharge);
            }
        } catch (ClientException e) {
            throw e;
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new ServerException();
        }
        return retInfo;
    }

    // 创建订单
    private Charge createOrder(Bill bill) {
        Map<String, Object> chargeParams = new HashMap<String, Object>();
        Pingpp.apiKey = this.pingSecretKey;
        chargeParams.put("order_no", bill.getOrderNo());
        logger.info("=========== is test ==========="+"yes".equals(pingTest));
        //测试环境，支付金额为0.01
        BigDecimal money = bill.getPayMoney();
        if ("yes".equals(pingTest)) {
            money = new BigDecimal(0.01);
            logger.info("money="+money.floatValue());
        }
        chargeParams.put("amount", money.multiply(new BigDecimal(100)).intValue());  //单位为分
        Map<String, String> app = new HashMap<String, String>();
        app.put("id", this.pingAppId);
        chargeParams.put("app", app);
        chargeParams.put("channel", bill.getChannel());  //微信wx
        chargeParams.put("currency", "cny");
        chargeParams.put("client_ip", bill.getClientIp());
        chargeParams.put("subject", bill.getActionName());
        chargeParams.put("body", bill.getGoods_desc());

        Charge charge = null; //Ping++ Charge对象
        try {
            charge = Charge.create(chargeParams);
        } catch (AuthenticationException e) {
            logger.error(e.getMessage(), e);
            throw new ServerException("支付接口连接失败");
        } catch (InvalidRequestException e) {
            logger.error(e.getMessage(), e);
            throw new ServerException("支付接口连接失败");
        } catch (APIConnectionException e) {
            logger.error(e.getMessage(), e);
            throw new ServerException("支付接口连接失败");
        } catch (APIException e) {
            logger.error(e.getMessage(), e);
            throw new ServerException("支付接口连接失败");
        } catch (ChannelException e) {
            logger.error(e.getMessage(), e);
            throw new ServerException("支付接口连接失败");
        }
        Order order = new Order(charge);
        orderDao.insert(order);
        return charge;
    }

    // 支付 的核心方法，后续步骤都使用Bill对象中的数据
    private Bill createBill(CreateOrderRequest req, Student student, String clientIp, PayThirdPart payThirdPart) {
        String goodsCategoryCode = req.getGoodsCategoryCode();
        String payType = req.getPayType();      //支付类型：1.余额支付；2.余额 + P++ 组合支付；3.P++支付
        String channel = req.getChannel();
        Integer goodsId = req.getGoodsId();
        BigDecimal userPayMoney = req.getMoney();// app客户端传过来的用户应支付价格
        Bill bill = new Bill();

        GoodsCategory goodsCategory = GoodsCategory.getByCode(goodsCategoryCode);
        // 购买1v1课程
        if (GoodsCategory.COURSE.getCode().equals(goodsCategoryCode)) {
            // 查询商品 --  套餐
            if (goodsId == null) {
                throw new ClientException("无效的商品，goodsId:" + goodsId);
            }
            Setmeal setmeal = paymentDao.findCourseSetmealById(goodsId);
            if (setmeal == null) {
                throw new ClientException("无效的商品，goodsId:" + goodsId);
            }
            String goodsName = setmeal.getSet_name(); // 商品名称
            BigDecimal goodsPrice = setmeal.getSet_amout();// 商品价格

            // 初始化订单基本信息，（根据支付方式不同，进行账单值的计算）
            bill = initStudentBill(goodsId, goodsName, goodsPrice, payType, channel, userPayMoney, goodsCategory, student);

        }
        // 购买系统课
        else if (GoodsCategory.SYS_COURSE.getCode().equals(goodsCategoryCode)) {
            // 查询商品 -- 微课
            if (goodsId == null) {
                throw new ClientException("无效的商品，goodsId:" + goodsId);
            }
            Weike weike = weikeDao.getById(Long.valueOf(goodsId));
            if (weike == null) {
                throw new ClientException("无效的商品，goodsId:" + goodsId);
            }
            String goodsName = weike.getTitle_student(); // 商品名称
            BigDecimal goodsPrice = weike.getStudent_price();// 商品价格
            bill = initStudentBill(goodsId, goodsName, goodsPrice, payType, channel, userPayMoney, goodsCategory, student);
        }
        // 购买系统课包月会员
        else if (GoodsCategory.SYS_COURSE_VIP_YEAR.getCode().equals(goodsCategoryCode)) {
            String goodsName = "购买系统课包月会员";

            // 年度会员价格
            SystemConfig systemCourseVipPriceForYear = systemConfigDao.findByCode("systemCourseVipPriceForYear");
            BigDecimal goodsPrice = new BigDecimal(systemCourseVipPriceForYear.getValue());// 套餐价格（所购商品价格）

            bill = initStudentBill(goodsId, goodsName, goodsPrice, payType, channel, userPayMoney, goodsCategory, student);

        }
        // 充值
        else if (GoodsCategory.RECHARGE.getCode().equals(goodsCategoryCode)) {
            String goodsName = "外教君充值";
            BigDecimal goodsPrice = req.getMoney();// 套餐价格（所购商品价格）
            if (goodsPrice.compareTo(new BigDecimal(0)) <= 0) {
                throw new ClientException("充值金额必须大于0, 您输入的金额：" + goodsPrice);
            }
            bill = initStudentBill(goodsId, goodsName, goodsPrice, payType, channel, userPayMoney, goodsCategory, student);
        } else {
            throw new ClientException("无效的商品类别" + goodsCategoryCode);
        }

        bill.setClientIp(clientIp);
        bill.setMembId(student.getMemb_id());
        bill.setThirdPart(payThirdPart.getName());
        return bill;
    }

    //(goodsType, goodsId, goodsName, goodsPrice, payType, channel, userPayMoney, goodsCategory, student);
    private Bill initStudentBill(Integer goodsId, String goodsName, BigDecimal goodsPrice, String payType, String channel, BigDecimal userPayMoney, GoodsCategory goodsCategory, Student student) {
        // 余额支付：支付金额 = 0
        // 第三方支付：支付金额 = 商品价格
        // 混合支付：支付金额 = 商品价格 - 余额

        BigDecimal membBalance = student.getMemb_balance();// 学生钱包余额
        Bill bill = new Bill();

        bill.setActionType(goodsCategory.getInOut());
        bill.setActionName(goodsCategory.getName());
        bill.setActionIconUrl(goodsCategory.getIcon());

        // 余额支付
        if (payType.equals(PayType.WALLET)) {
            BigDecimal payMoney = new BigDecimal(0); // 余额支付：支付金额 = 0
            bill.setPayMoney(goodsPrice); // #支付金额

            BigDecimal balanceAfter = membBalance.subtract(goodsPrice); // 扣款后的余额 = 余额 - 商品价格
            // 检查余额是否充足
            if (balanceAfter.doubleValue() < 0) {
                throw new ClientException("余额不足。当前余额：" + membBalance + ", 商品价格：" + goodsPrice);
            }
            bill.setBalanceBefore(membBalance);
            bill.setBalanceChange(goodsPrice);
            bill.setBalanceAfter(balanceAfter);
            bill.setChannel("wallet");// #支付渠道 wx,alipay
        }
        // 第三方支付
        else if (payType.equals(PayType.THIRDPART)) {
            BigDecimal payMoney = goodsPrice;// 第三方支付：支付金额 = 商品价格
            if (payMoney.compareTo(userPayMoney) != 0) {
                throw new ClientException("支付金额有误。商品价格：" + goodsPrice + ", 支付金额：" + userPayMoney);
            }
            bill.setPayMoney(payMoney); // #支付金额

            bill.setBalanceBefore(membBalance);
            // 如果是充值，余额增加金额
            if (GoodsCategory.RECHARGE.getCode().equals(goodsCategory.getCode())) {
                bill.setBalanceChange(payMoney);// 如果是充值，余额变动金额payMoney
                bill.setBalanceAfter(membBalance.add(payMoney));
            } else {
                bill.setBalanceChange(new BigDecimal(0));// 余额变动金额0 ，全部使用第三方支付，余额不不变动
                bill.setBalanceAfter(membBalance);
            }
            bill.setChannel(channel);// #支付渠道 wx,alipay
        }
        // 组合支付（余额 + 第三方）
        else if (payType.equals(PayType.MULTIPLE)) {
            BigDecimal payMoney = goodsPrice.subtract(membBalance);// 应支付金额 （混合支付：支付金额 = 商品价格 - 余额）
            if (payMoney.compareTo(userPayMoney) != 0) {
                throw new ClientException("实际支付金额误。商品价格：" + goodsPrice + ", 余额：" + membBalance + ", 应支付金额：" + payMoney + ", 实际支付金额：" + userPayMoney);
            }
            bill.setPayMoney(payMoney); // #支付金额

            bill.setBalanceBefore(membBalance);
            bill.setBalanceChange(membBalance);// 余额变动金额0 ，全部使用第三方支付，余额不不变动
            bill.setBalanceAfter(new BigDecimal(0));

            bill.setChannel(channel);// #支付渠道 wx,alipay
        }

        bill.setAmount(goodsPrice);//交易总金额 （= 支付金额 + 余额变动金额）也=商品价格

        bill.setGoods_desc(goodsName);
        bill.setGoodsId(goodsId);
        bill.setGoodsCategoryCode(goodsCategory.getCode());

        bill.setPayType(payType);
        bill.setOrderNo(OrderUtil.getOrderNumber());// 订单号（交易单号）
        bill.setCreateOrderTime(new Date());// 订单创建时间（交易时间）
        bill.setPaymentStatus(PaymentConstants.Status.UN_PAY);// 订单支付状态：1待付款，2用户取消支付，3支付失败，4支付成功

        return bill;
    }

    // 支付成功后，添加已购商品记录
    private void addGoods(Bill bill) {

        // 添加套餐
        if (GoodsCategory.COURSE.getCode().equals(bill.getGoodsCategoryCode())) {
            // 添加商品：学生购买的套餐
            addCourse(bill, false);

            // 更新套餐购买人数:+1
            paymentDao.updateSetmealBuyNumber(bill.getGoodsId());

            // 课程学习人数+1(ts_nt_course_info.study_size+1)
            paymentDao.updateCourseStudyNumber(bill.getGoodsId());
        }
        // 添加 购买系统课
        else if (GoodsCategory.SYS_COURSE.getCode().equals(bill.getGoodsCategoryCode())) {

            //3. 添加学生购买微课记录
            Weike weike = weikeDao.getById(Long.valueOf(bill.getGoodsId()));
            if (weike == null) {
                logger.error("购买微课失败，微课不存在：微课Id = " + bill.getGoodsId());
            } else {
                //5. 给老师提成
                Boolean isCharge = chargeForTeacher(weike);

                WeikeStudentBuy weikeStudentBuy = new WeikeStudentBuy();
                weikeStudentBuy.setPayment_status(Integer.parseInt(Constants.PayState.success));
                weikeStudentBuy.setWeike_id(weike.getId());
                weikeStudentBuy.setMemb_id(bill.getMembId());
                weikeStudentBuy.setDeleted(Constants.Deleted.NO);
                Date date = new Date();
                weikeStudentBuy.setCreate_date(date);
                weikeStudentBuy.setCreate_user(bill.getMembId());
                weikeStudentBuy.setStudy_number(0L);
                weikeStudentBuy.setUpdate_date(date);
                weikeStudentBuy.setUpdate_user(bill.getMembId());
                if (isCharge) {
                    weikeStudentBuy.setTeacher_charged(1);
                } else {
                    weikeStudentBuy.setTeacher_charged(0);
                }
                weikeStudentBuyDao.insert(weikeStudentBuy);

                //4. 更新微课购买人数buy_number
                weikeDao.updateBuyNumber(weike.getId());
            }

            logger.info("购买微课 end");
        }
        // 购买系统课包月会员,重复购买累加时间
        else if (GoodsCategory.SYS_COURSE_VIP_YEAR.getCode().equals(bill.getGoodsCategoryCode())) {
            /**
             * 考虑重复因素的购买会员起止时间计算规则：
             * 上次会员起止时间：T1，E1
             * 这次购买会员时间：Now
             * 这次会员起止时间：T,E
             * if Now <= E1  ==> T = T1;E = E1 + 1year;
             * if Now > E1   ==> T = Now;E = Now + 1year;
             */
            //查询有效的会员记录
            WeikeSubscription validSubscription = weikeSubscriptionDao.findValidSubscribedByMembId(bill.getMembId());
            if (validSubscription != null) {
                validSubscription.setSubscription_end_time(DateUtil.addMonth(validSubscription.getSubscription_end_time(), 1));
                weikeSubscriptionDao.update(validSubscription);
            } else {
                // 新增微课VIP会员记录
                WeikeSubscription weikeSubscription = new WeikeSubscription();
                weikeSubscription.setMemb_id(bill.getMembId());
                weikeSubscription.setPay_status(bill.getPaymentStatus());
                Date now = new Date();
                weikeSubscription.setSubscription_start_time(now);
                weikeSubscription.setSubscription_end_time(DateUtil.addMonth(now, 1));
                weikeSubscriptionDao.insert(weikeSubscription);
            }


            //学生购买会员赠送套餐
//            SystemConfig systemConfig = systemConfigDao.findByCode("buyVipPresentSetmeal");
//            if (systemConfig == null) {
//                logger.warn("没有要赠送的套餐");
//            } else {
//                Integer setmealId = Integer.parseInt(systemConfig.getValue());
//                Bill b = new Bill();
//                b.setGoodsId(setmealId);// setmealId赠送套餐Id
//                b.setMembId(bill.getMembId());
//                b.setId(bill.getId());
//                addCourse(b, true);
//            }

        }
        // 充值
        else if (GoodsCategory.RECHARGE.getCode().equals(bill.getGoodsCategoryCode())) {
            // 充值没有商品添加，不添加任何商品
        } else {
            throw new ClientException("无效的商品类别" + bill.getGoodsCategoryCode());
        }
    }

    private Boolean chargeForTeacher(Weike weike) {
        TdNtTeacherResult teacher = tdNtTeacherDao.getById(weike.getCreate_user());
        if (teacher == null) {
            logger.info("老师不存在，不给提成 tea_id = " + weike.getCreate_user() + ",weike_id = " + weike.getId());
            return false;
        }

        // 查询微课的已提成语音总时长CD(chargeDuration)(小时)
        Integer chargeDuration = weikeTeacherHourChargeDao.findChargeTotalDuration(weike.getId());
        if (chargeDuration < 10) {
            logger.info("老师微课提成，已提成时长chargeDuration:" + chargeDuration + " < 10,不符合按照购买数量提成规则。学生购买微课不给老师提成。weike_id=" + weike.getId());
            return false;
        }

        // 2. 添加老师账单
        BigDecimal balance = teacher.getTea_balance();   // 查询老师账户余额（)
        if (balance == null) {
            balance = new BigDecimal(0);
        }
        TdNtTeaFoundChange teaFoundChange = new TdNtTeaFoundChange();
        teaFoundChange.setTea_id(weike.getCreate_user());
        teaFoundChange.setState(Constants.PayState.success);
        teaFoundChange.setTfchg_front_amount(balance);
        BigDecimal earned = new BigDecimal(5);  //每个学生提成5美元
        teaFoundChange.setTfchg_back_amount(balance.add(earned));  //设置变化后的金额
        teaFoundChange.setTfchg_change_amount(earned);
        Date now = new Date();
        Timestamp now2 = new Timestamp(now.getTime());
        teaFoundChange.setTfchg_change_month(DateUtil.getMonth(now));
        teaFoundChange.setTfchg_change_time(now2);
        teaFoundChange.setTfchg_change_type(Constants.FundFlowType.in);//
        teaFoundChange.setTfchg_account(new BigDecimal(0.00d));//老师提现时使用
        teaFoundChange.setTfchg_counter_fee(new BigDecimal(0.00d));//老师提现时使用
        teaFoundChange.setTfchg_number(OrderUtil.getOrderNumber());//订单号
        teaFoundChange.setType(Constants.fundChangeTypeForTeacher.weike_code);
        teaFoundChange.setTfchg_change_name(Constants.fundChangeTypeForTeacher.weike_msg);
        tdNtTeaFoundChangeDao.insert(teaFoundChange);

        ///3. 更新老师余额
        tdNtTeacherDao.addBalance(weike.getCreate_user(), earned);
        logger.info(" ---------- 计费 end -----------");
        return true;

    }

    // 购买VIP 赠送课程时也调用此方法赠送课程 isGifg：是否是赠送的课程
    private void addCourse(Bill bill, boolean isGift) {
        Setmeal goods = paymentDao.findTsNtSetmeal(bill.getGoodsId());//查询出商品信息：课程套餐
        Setmeal setmeal = new Setmeal();
        setmeal.setCou_id(goods.getCou_id());
        setmeal.setEnd_time(new Timestamp(DateUtil.getTime(goods.getSet_type(), goods.getSize())));
        setmeal.setMemb_id(bill.getMembId());
        setmeal.setMinute_amout(goods.getSet_amout().divide(new BigDecimal(goods.getSet_time()), 2, BigDecimal.ROUND_HALF_EVEN));// 每分钟价格
        setmeal.setOriginal_price(goods.getOriginal_price());
        setmeal.setSet_amout(goods.getSet_amout());
        setmeal.setSet_id(goods.getSet_id());
        setmeal.setSet_limit(goods.getSet_limit());
        setmeal.setSet_name(goods.getSet_name());
        setmeal.setSet_synopis(goods.getSet_synopis());
        setmeal.setSet_time(goods.getSet_time());
        setmeal.setSet_type(goods.getSet_type());
        setmeal.setSurplus_time(goods.getSet_time());
        setmeal.setSet_picture(goods.getSet_picture());
        setmeal.setSize(goods.getCou_size());// 套餐课时数
        setmeal.setSet_reserve_size(goods.getCou_size());
        setmeal.setState(Constants.SetMealPayState.paid);// 支付状态
        setmeal.setMfchg_id(bill.getId());// 订单号
        setmeal.setCou_time(goods.getCou_time());// 1节课的时长
        if (isGift) {
            setmeal.setAdditional(Constants.isGifg.YES);
        } else {
            setmeal.setAdditional(Constants.isGifg.NO);
        }

        paymentDao.addMembSetmeal(setmeal);
    }


    // Ping++异步回调
    @Transactional
    public synchronized void  notify(Map<String, Object> params) {
        ChargeNotifyLog pingNotifyLog = null;
        StringBuffer logAppend = new StringBuffer();
        try {
            Map<String, Object> data = (Map<String, Object>) params.get("data");
            Map<String, Object> paymentInfo = (Map<String, Object>) data.get("object");

            // 保存回调日志
            pingNotifyLog = new ChargeNotifyLog(paymentInfo);
            pingNotifyLog.setHttpPayload(JsonUtil.toJson(params));


            //Event 事件类型: charge.succeeded	支付对象，支付成功时触发。
            if (!"charge.succeeded".equals(params.get("type").toString())) {
                logger.error("不做任何处理 type=" + params.get("type").toString());
                logAppend.append("不做任何处理 type=" + params.get("type").toString());
                return;
            }

            boolean isPaid = (Boolean) paymentInfo.get("paid");                        //是否已经付款
            String orderNo = paymentInfo.get("order_no").toString();                   //支付编号
            //ping++ Webhooks 回调测试订单编号123456789
            if ("123456789".equals(orderNo)) {
                logAppend.append("测试订单 orderNo=" + orderNo);
                return;
            }

            // 1. 查询账单信息
            Bill bill = billDao.getByOrderNo(orderNo);
            if (bill == null) {
                logger.error("Ping++异步回调 --> 1. 账单不存在，不做业务处理。 orderNo：" + orderNo);
                logAppend.append("账单不存在，不做业务处理。 orderNo：" + orderNo);
                return;
            }

            // 2. 幂等处理：如果订单已经是支付成功，那么不对Ping++回调通知做任何业务处理
            if (PaymentConstants.Status.SUCCESS.intValue() == bill.getPaymentStatus().intValue()) {
                logger.info("Ping++异步回调 --> 2. 【幂等处理】订单已完成，不做业务处理。PaymentStatus:" + bill.getPaymentStatus());
                logAppend.append("【幂等处理】订单已完成，不做业务处理。PaymentStatus:" + bill.getPaymentStatus());
                return;
            }


            // 3. 业务处理
            // 更新订单状态
            Integer oldStatus = bill.getPaymentStatus();
            Integer newStatus = isPaid ? PaymentConstants.Status.SUCCESS : PaymentConstants.Status.UN_PAY;
            String thirdPartLogTag = "Ping++异步回调 --> ";

            processPayNotifyBusiness(logAppend, isPaid, bill, oldStatus, newStatus, thirdPartLogTag);
        } catch (Exception e) {
            logger.error("\nPing++Notfiy 异常\n", e);
            logAppend.append("Ping++Notfiy 异常" + e.getMessage()).append(";;");
        } finally {
            try {
                pingNotifyLog.setResult(logAppend.toString());
                chargeNotifyLogDao.insert(pingNotifyLog);
            } catch (Exception e) {
                logger.error("数据库操作Error for insert table 'payment_charge_notify_log':" + e.getMessage(), e);
            }
        }
    }

    private void processPayNotifyBusiness(StringBuffer logAppend, boolean isPaid, Bill bill, Integer oldStatus, Integer newStatus, String thirdPartLogTag) {
        logger.info(thirdPartLogTag + "1. 更新订单状态：" + bill.getPaymentStatus());
        billDao.updatePaymentStatus(bill.getOrderNo(), newStatus);
        logAppend.append(" 更新订单状态，PaymentStatus: " + oldStatus + " ---> " + newStatus).append(";;");

        // 已付款
        if (isPaid) {
            // 2. 变更余额
            logger.info(thirdPartLogTag + "2. 变更余额BalanceAfter:" + bill.getBalanceAfter() + ", membId:" + bill.getMembId());
            paymentDao.updateMenberBalance(bill.getMembId(), bill.getBalanceAfter());
            logAppend.append("  变更余额，membId:" + bill.getMembId() + "，balanceAfter: " + bill.getBalanceAfter()).append(";;");

            // 3. 添加商品
            logger.info(thirdPartLogTag + "3. 添加商品");
            addGoods(bill);
            logAppend.append(" 添加商品").append(";;");

            // 4. 充值赠送余额
            if (GoodsCategory.RECHARGE.getCode().equals(bill.getGoodsCategoryCode())) {
                rechargeGift(bill, logAppend);
            }
        } else {
            logger.error(thirdPartLogTag + "未支付isPaid：" + isPaid + ",orderNo:" + bill.getOrderNo() + ",（不变更余额，不添加商品）");
            logAppend.append(" 未支付isPaid：" + isPaid + ",orderNo:" + bill.getOrderNo() + ",（不变更余额，不添加商品）").append(";;");
        }

        logger.info(thirdPartLogTag + " /end. orderNo:" + bill.getOrderNo() + "\n\n");
        logAppend.append(thirdPartLogTag + " /end. orderNo:" + bill.getOrderNo() + "\n\n").append(";;");
    }

    // 充值赠送余额
    private void rechargeGift(Bill bill, StringBuffer logAppend) {
        List<PayActivity> activities = paymentDao.findPayActivities();
        Bill gift = new Bill();
        gift.setMembId(bill.getMembId());
        gift.setGoodsId(bill.getGoodsId());
        gift.setPaymentStatus(PaymentConstants.Status.SUCCESS);
        gift.setGoodsCategoryCode(GoodsCategory.recharge_gift);
        gift.setClientIp(bill.getClientIp());
        gift.setCreateOrderTime(new Date());
        gift.setOrderNo(OrderUtil.getOrderNumber());
        gift.setPayMoney(new BigDecimal(0));
        gift.setChannel("system_gift");
        gift.setActionType(PaymentConstants.ActionType.IN);
        gift.setActionName(PaymentConstants.StudentActionName.recharge_gift_msg);
        gift.setActionIconUrl("");
        gift.setPaymentDesc("充值赠送");
        gift.setBalanceBefore(bill.getBalanceAfter());
        gift.setPayType(bill.getPayType());

        gift.setGoods_desc("充值赠送");
        boolean isMatchCondition = false; // 是否符合赠送的条件
        if (activities != null) {
            for (PayActivity act : activities) {
                logger.info("是否符合充值赠送条件：活动金额：" + act.getRecharge_amount() + ", 付款金额：" + bill.getPayMoney() + "，结果" + (bill.getPayMoney().compareTo(act.getRecharge_amount()) >= 0));
                if (bill.getPayMoney().compareTo(act.getRecharge_amount()) >= 0) {
                    gift.setPayMoney(act.getGive_amount());
                    gift.setAmount(act.getGive_amount());
                    gift.setBalanceChange(act.getGive_amount());
                    gift.setBalanceAfter(gift.getBalanceBefore().add(act.getGive_amount()));
                    gift.setPaymentDesc("充值赠送: 冲" + act.getRecharge_amount() + "送" + act.getGive_amount());
                    isMatchCondition = true;
                    break;
                }
            }
        }


        if (isMatchCondition) {
            // 保存订单
            billDao.insert(gift);
            logAppend.append("充值赠送 --》  保存赠送金额的虚拟订单" + gift.getOrderNo()).append(";;");

            //余额变更
            paymentDao.updateMenberBalance(bill.getMembId(), gift.getBalanceAfter());
            logAppend.append("充值赠送 --》  变更余额，membId:" + gift.getMembId() + "，balanceAfter: " + gift.getBalanceAfter()).append(";;");
        }
    }

    public PageResponse wallet(PageRequest req, Integer memb_id) {
        PageResponse pageResponse = new PageResponse(req.getPageSize(), req.getCurrentPage());

        //查询总数
        Long total = billDao.findByMembIdCount(memb_id);
        pageResponse.setTotal(total);

        RowBounds rowBounds = new RowBounds(pageResponse.getOffset().intValue(), pageResponse.getPageSize().intValue());
        List<Bill> billList = billDao.findByMembId(rowBounds, memb_id);
        if (billList == null) {
            billList = new ArrayList<>();
        }

        pageResponse.setData(billList);

        return pageResponse;
    }

    public BigDecimal getBalance(Integer memb_id) {
        Student student = studentDao.getById(memb_id);
        return student.getMemb_balance();
    }

    public CreateWeixinPayOrderResponse studentCreateOrderForWeixinPay(CreateWeixinPayOrderRequest req, Integer memb_id, String ip) {
        CreateWeixinPayOrderResponse order = null;
        try {
            Student student = studentDao.getById(memb_id);

            // 1. 创建账单
            Bill bill = createBill(req, student, ip, PayThirdPart.WeChatPay);

            // 余额支付
            if (PayType.WALLET.equals(req.getPayType())) {
                bill.setPaymentStatus(PaymentConstants.Status.SUCCESS);

                // 2. 保存账单
                billDao.insert(bill);

                // 3. 变更余额
                paymentDao.updateMenberBalance(student.getMemb_id(), bill.getBalanceAfter());

                // 4. 添加商品
                addGoods(bill);
            }
            // 第三方支付 或 组合支付（余额 + 第三方）
            else {
                // 2. 调用微信服务器接口：统一下单支付API创建订单
                order = createWeiXinPayOrder(bill, req.getCode());

                // 3. 保存账单
                billDao.insert(bill);

                // 添加商品相关的业务逻辑在WeiXinPay回调方法里处理
            }
        } catch (ClientException e) {
            throw e;
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw new ServerException(e.getMessage());
        }
        return order;
    }

    // 调用微信服务器接口：统一下单支付API创建订单，URL地址：https://api.mch.weixin.qq.com/pay/unifiedorder
    private CreateWeixinPayOrderResponse createWeiXinPayOrder(Bill bill, String jsCode) {
        // 创建请求数据
        Map<String, String> apiParams = new HashMap<>();
        apiParams.put("appid", smallProgramParameter.getAppId());
        apiParams.put("mch_id", smallProgramParameter.getMch_id());
        apiParams.put("nonce_str", UUID.randomUUID().toString().replaceAll("-", "").toUpperCase());
        apiParams.put("body", "外教君-" + bill.getActionName());
        apiParams.put("out_trade_no", bill.getOrderNo());

        if (this.pingTest.equalsIgnoreCase("yes")) {
            apiParams.put("total_fee", "1");// 测试支付1分钱
        } else {
            apiParams.put("total_fee", bill.getPayMoney().multiply(new BigDecimal(100)).longValue()+"");
        }
        apiParams.put("spbill_create_ip", bill.getClientIp());
        apiParams.put("notify_url", "http://app.waijiaojun.com:8082/weike/wechatSmallProgram/WeiChatPay/notify");
        apiParams.put("trade_type", "JSAPI");
        try {
            String openId = wechatSmallProgramService.getRealOpenId(jsCode);
            apiParams.put("openid", openId);
        } catch (IOException e) {
            logger.error(e.getMessage(), e);
            throw new ServerException("支付失败：获取微信openId失败");
        }

        // 请求微信支付创建预订单api,
        String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
        String responseStr = HttpUtil.httpPost(url, WeixinPaySignUtil.getXmlStr(apiParams, smallProgramParameter.getApi_key()));
        WeixinUnifiedorderResponse response = XmlUtil.fromXml(responseStr, WeixinUnifiedorderResponse.class);
        if (!"SUCCESS".equalsIgnoreCase(response.getReturn_code())) {
            throw new ServerException("支付失败：" + response.getReturn_msg());
        } else if (!"SUCCESS".equalsIgnoreCase(response.getResult_code())) {
            throw new ServerException("支付失败：[" + response.getErr_code() + "] " + response.getErr_code_des());
        }

        CreateWeixinPayOrderResponse orderResponse = new CreateWeixinPayOrderResponse();
        orderResponse.setAppId(smallProgramParameter.getAppId());
        orderResponse.setData("prepay_id=" + response.getPrepay_id());
        orderResponse.setNonceStr(UUID.randomUUID().toString().replaceAll("-", "").toUpperCase());
        orderResponse.setSignType("MD5");
        orderResponse.setTimeStamp(System.currentTimeMillis() + "");

        Map<String, String> param = new HashMap<>();
        param.put("appId", orderResponse.getAppId());
        param.put("nonceStr", orderResponse.getNonceStr());
        param.put("package", orderResponse.getData());
        param.put("signType", orderResponse.getSignType());
        param.put("timeStamp", orderResponse.getTimeStamp());
        String sign = WeixinPaySignUtil.payUniSign(param, smallProgramParameter.getApi_key());
        orderResponse.setPaySign(sign);

        return orderResponse;
    }

    public static void main(String[] args) {
        WeixinUnifiedorderRequest orderRequest = new WeixinUnifiedorderRequest();
        orderRequest.setAppid("appid");
        orderRequest.setMch_id("mch_id");
        orderRequest.setNonce_str("nonce_str");
        orderRequest.setSign("sign");
        orderRequest.setBody("body");
        orderRequest.setOut_trade_no("out_trade_no");
        orderRequest.setTotal_fee(88888);
        orderRequest.setSpbill_create_ip("spbill_create_id");
        orderRequest.setNotify_url("http://app.waijiaojun.com:8082/weike/payment/WeiChatPay/notify");
        orderRequest.setTrade_type("JSAPI");


        orderRequest.setOpenid("openId");
        orderRequest.setTime_start(System.currentTimeMillis() + "");
        System.out.println(XmlUtil.toXml(orderRequest));

        String res = new BigDecimal(1.0000001).multiply(new BigDecimal(100)).longValue()+"";
        System.out.println(res);
    }

    /**
     * 微信支付回调
     *
     * @param pojo
     */
    public void notifyForWeiChatPay(WeiChatPayNotifyRequest pojo) {
        StringBuffer logAppend = new StringBuffer();
        String thirdPartLogTag = "微信支付异步回调 --> ";
        try {
            if (!"SUCCESS".equalsIgnoreCase(pojo.getReturn_code())) {
                String msg = "【微信支付回调错误】- 通信错误：return_code=" + pojo.getReturn_code() + ", return_msg=" + pojo.getReturn_msg();
                logger.error(msg);
                logAppend.append(msg).append(";;");
            } else if (!"SUCCESS".equalsIgnoreCase(pojo.getResult_code())) {
                String msg = "【微信支付回调错误】- 业务结果错误：result_code=" + pojo.getResult_code() + ", err_code=" + pojo.getErr_code() + ", err_code_des=" + pojo.getErr_code_des();
                logger.error(msg);
                logAppend.append(msg).append(";;");
            } else {
                //支付成功处理业务
                boolean isPaid = true;
                String orderNo = pojo.getOut_trade_no();
                //支付编号，订单号
                // 1. 查询账单信息
                Bill bill = billDao.getByOrderNo(orderNo);
                if (bill == null) {
                    logger.error(thirdPartLogTag + "1. 账单不存在，不做业务处理。 orderNo：" + orderNo);
                    logAppend.append("账单不存在，不做业务处理。 orderNo：" + orderNo).append(";;");
                    return;
                }

                // 2. 幂等处理：如果订单已经是支付成功，那么不对Ping++回调通知做任何业务处理
                if (PaymentConstants.Status.SUCCESS.intValue() == bill.getPaymentStatus().intValue()) {
                    logger.info(thirdPartLogTag + "2. 【幂等处理】订单已完成，不做业务处理。PaymentStatus:" + bill.getPaymentStatus());
                    logAppend.append("【幂等处理】订单已完成，不做业务处理。PaymentStatus:" + bill.getPaymentStatus()).append(";;");
                    return;
                }

                Integer oldStatus = bill.getPaymentStatus();
                Integer newStatus = PaymentConstants.Status.SUCCESS;

                processPayNotifyBusiness(logAppend, isPaid, bill, oldStatus, newStatus, thirdPartLogTag);
            }
        } catch (Exception e) {
            logger.error(thirdPartLogTag + " 异常\n", e);
            logAppend.append(thirdPartLogTag + " 异常" + e.getMessage()).append(";;");
        } finally {
            try {
                pojo.setResult(logAppend.toString());
                weiChatPayNotifyRequestDao.insert(pojo);
            } catch (Exception e) {
                logger.error("数据库操作Error for insert table 'payment_wei_chat_pay_notify_request':" + e.getMessage(), e);
            }
        }

    }



    public GetPaymentDetail paypalDetail(String paypalId){
        GetPaymentDetail getPaymentDetail = new GetPaymentDetail();
        String response = "";
        try {
            // Authorization Code and Co-relationID retrieved from Mobile SDK.
//            String authorizationCode = "C101.Rya9US0s60jg-hOTMNFRTjDfbePYv3W_YjDJ49BVI6YJY80HvjL1C6apK8h3IIas.ZWOGll_Ju62T9SXRSRFHZVwZESK";
//            String orderId = "PAY-77S07578D7632515RLLI2NDQ";
            String authHttp = httpPostMethod(oath2TokenUrl,paypalAppId,paypalSecretKey).toString();
            GetAccessToken getAccessToken = JsonUtil.toObject(authHttp,GetAccessToken.class);
            response = sendGet(new StringBuilder(payDetailUrl).append(paypalId).toString(),getAccessToken.getAccess_token());
            getPaymentDetail = JsonUtil.toObject(response,GetPaymentDetail.class);
        } catch (Exception e) {
            logger.error("调用paypal支付失败" + response.toString());
            logger.error(e.getMessage(),e);
        }
        return getPaymentDetail;
    }


    /**
     * 获取token
     * @param uri
     * @return
     */
    public static String httpPostMethod(String uri,String paypalAppId,String paypalSecretKey) {
        String body = "";
        //创建客户端访问服务器的httpclient对象   打开浏览器
        HttpClient httpclient = new DefaultHttpClient();
        //2.以请求的连接地址创建get请求对象     浏览器中输入网址
        HttpPost httpPost = new HttpPost(uri);
        InputStream input;
        logger.info("执行请求： " + httpPost.getURI());
        try {
            // Execute the method.
            String encoding = DatatypeConverter.printBase64Binary(new StringBuffer(paypalAppId).append(":").append(paypalSecretKey).toString().getBytes("UTF-8"));
            httpPost.setHeader("Authorization", "Basic " +encoding);

            List<BasicNameValuePair> pairList = new ArrayList<BasicNameValuePair>();
            pairList.add(new BasicNameValuePair("grant_type", "client_credentials"));
            httpPost.setEntity(new UrlEncodedFormEntity(pairList, "utf-8"));
            //向服务器端发送请求 并且获取响应对象  浏览器中输入网址点击回车
            HttpResponse response = httpclient.execute(httpPost);
            StatusLine statusLine = (StatusLine) response.getStatusLine();//获取请求对象中的响应行对象
            int statusCode = statusLine.getStatusCode();//从状态行中获取状态码
            logger.info("状态码：" + statusCode);
            if (statusCode != HttpStatus.SC_OK) {
                logger.error("Method failed: " + response.getStatusLine());
            }
            // Use caution: ensure correct character encoding and is not binary data
            //5.  可以接收和发送消息
            HttpEntity entity = response.getEntity();
            //6.从消息载体对象中获取操作的读取流对象
            input = entity.getContent();
            BufferedReader br = new BufferedReader(new InputStreamReader(input));
            String str1 = br.readLine();
            String result = new String(str1.getBytes("gbk"), "utf-8");
            body = result;
            System.out.println("服务器的响应是:" + result);
            br.close();
            input.close();
            httpPost.releaseConnection();
            logger.info(new String(response.getStatusLine().toString()));
        } catch (HttpException e) {
            logger.error("调用paypal token接口失败");
            logger.error(e.getMessage(), e);
        } catch (IOException e) {
            logger.error("调用paypal token接口失败");
            logger.error(e.getMessage(), e);
        }
        return body;
    }
    /**
     * 获取支付结果
     * @param uri
     * @param access_token
     * @return
     */
    public static String sendGet(String uri,String access_token) {
        String result = "";
        BufferedReader in = null;
        try {
            String urlNameString = uri;
            URL realUrl = new URL(urlNameString);
            // 打开和URL之间的连接
            URLConnection connection = realUrl.openConnection();
            // 设置通用的请求属性
            connection.setRequestProperty("Content-Type","application/json");
            connection.setRequestProperty("Authorization","Bearer "+access_token);
            // 建立实际的连接
            connection.connect();
            // 获取所有响应头字段
            Map<String, List<String>> map = connection.getHeaderFields();
            // 遍历所有的响应头字段
//            for (String key : map.keySet()) {
//                System.out.println(key + "--->" + map.get(key));
//            }
            // 定义 BufferedReader输入流来读取URL的响应
            in = new BufferedReader(new InputStreamReader(
                    connection.getInputStream()));
            String line;
            while ((line = in.readLine()) != null) {
                result += line;
            }
        } catch (Exception e) {
            System.out.println("发送GET请求出现异常！" + e);
            e.printStackTrace();
        }
        // 使用finally块来关闭输入流
        finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (Exception e2) {
                e2.printStackTrace();
            }
        }
        return result;
    }

}
